package com.xxl.job.executor.utils;

import io.tus.java.client.TusClient;
import io.tus.java.client.TusURLMemoryStore;
import io.tus.java.client.TusUpload;
import io.tus.java.client.TusUploader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

//import com.springboot.hisense.plan.dao.SysFileUploadInfoMapper;
//import com.springboot.hisense.plan.model.SysFileUploadInfo;

/**
 * 类功能说明：与TUS文件服务交互，实现文件的处理（上传、下载、删除）
 *
 */

public class TusFileUtils {
    public static Logger logger = LoggerFactory.getLogger(TusFileUtils.class);

    private final static String TUS_HTTP_URL = "tus.http.url";
    private final static String TUS_CHUNK_SIZE = "tus.chunk.size";

    /**
     * 方法说明：初始化TUS服务的client
     *
     * @param
     * @return TusClient
     * @throws
     */
    public static TusClient getTusClient(String tusdUrl) {

        // When Java's HTTP client follows a redirect for a POST request, it will change the
        // method from POST to GET which can be disabled using following system property.
        // If you do not enable strict redirects, the tus-java-client will not follow any
        // redirects but still work correctly.
        System.setProperty("http.strictPostRedirect", "true");

        // Create a new TusClient instance
        TusClient tusClient = new TusClient();

        // Configure tus HTTP endpoint. This URL will be used for creating new uploads
        // using the Creation extension
        try {
            tusClient.setUploadCreationURL(new URL(tusdUrl));
        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            logger.error("connect to tus server url MalformedURLException ", e);
        }

        // Enable resumable uploads by storing the upload URL in memory
        tusClient.enableResuming(new TusURLMemoryStore());

        return tusClient;
    }


    /**
     * 方法说明：      将文件上传到tus服务，并将返回的文件ID与具体的业务实体关联方便后续的查询维护
     *
     * @param fileBusiType 文件关联的业务实体ID，如在自己表中维护，请传null
     * @param referenceId  文件所代表的业务类型，如在自己表中维护，请传null
     * @param file         上传文件
     * @return boolean      是否操作成功
     * @throws
     */
//    public static boolean fileUpload(final String referenceId, final Integer fileBusiType, final File file) throws Exception {
//        //首先对参数进行检查
//        if (file == null) {
//            return false;
//        } else {
//
//            // Open a file using which we will then create a TusUpload. If you do not have
//            // a File object, you can manually construct a TusUpload using an InputStream.
//            // See the documentation for more information.
//            final TusUpload upload = new TusUpload(file);
//
//            logger.info("File upload start.....");
//
//            // We wrap our uploading code in the TusExecutor class which will automatically catch
//            // exceptions and issue retries with small delays between them and take fully
//            // advantage of tus' resumability to offer more reliability.
//            // This step is optional but highly recommended.
//
//            TusExecutor executor = new TusExecutor() {
//                @Override
//                protected void makeAttempt() throws ProtocolException, IOException {
//                    // First try to resume an upload. If that's not possible we will create a new
//                    // upload and get a TusUploader in return. This class is responsible for opening
//                    // a connection to the remote server and doing the uploading.
//                    TusClient tusClient = getTusClient();
//                    final TusUploader uploader = tusClient.resumeOrCreateUpload(upload);
//
//                    // Upload the file in chunks of 1KB sizes.
//                    uploader.setChunkSize(1024);
//                    // Upload the file as long as data is available. Once the
//                    // file has been fully uploaded the method will return -1
//                    do {
//                        // Calculate the progress using the total size of the uploading file and
//                        // the current offset.
//                        long totalBytes = upload.getSize();
//                        long bytesUploaded = uploader.getOffset();
//                        double progress = 100;
//                        if (totalBytes != 0) {
//                            progress = (double) bytesUploaded / totalBytes * 100;
//                        }
//                        logger.info(String.format("Upload at %06.2f%% %n", progress));
//                    } while (upload.getSize() > 0 && uploader.uploadChunk() > -1);
//
//                    // Allow the HTTP connection to be closed and cleaned up
//                    uploader.finish();
//
//                    logger.info("Upload finished.");
//                    logger.info(String.format("Upload available at: %s", uploader.getUploadURL().toString()));
//
//                    // when the file upload finish we should save information in database
//                    SysFileUploadInfo fileUploadInfo = new SysFileUploadInfo();
//                    // obtain upload file id  http://localhost:1080/files/233ac50120ebd1f2b12df3bdf44b6e90 file id is 233ac50120ebd1f2b12df3bdf44b6e90
//                    fileUploadInfo.setUploadId(CmUtil.getUUID());
//                    fileUploadInfo.setFileUploadId(StringUtils.substring(uploader.getUploadURL().toString(), StringUtils.lastIndexOf(uploader.getUploadURL().toString(), "/") + 1));
//                    fileUploadInfo.setFileName(file.getName());
//                    fileUploadInfo.setReferenceId(referenceId);
//                    fileUploadInfo.setFileBusiType(fileBusiType);
//                    fileUploadInfo.setDelFlag(0);
//                    fileUploadInfo.setCreatedBy(CmUtil.getUserId());
//                    fileUploadInfo.setCreatedDate(CmUtil.date2day(null));
//                    fileUploadInfo.setUpdatedBy(CmUtil.getUserId());
//                    fileUploadInfo.setUpdatedDate(CmUtil.date2day(null));
//                    // save fileUploadInfo entity in database
//                    SysFileUploadInfoMapper fileUploadInfoMapper = (SysFileUploadInfoMapper) SpringContextUtil.getBean("sysFileUploadInfoMapper");
//                    fileUploadInfoMapper.insertSelective(fileUploadInfo);
//                }
//            };
//            try {
//                executor.makeAttempts();
//            } catch (Exception e) {
//                e.printStackTrace();
//            }
//        }
//        return true;
//    }

    /**
     * 方法说明：      将文件上传到tus服务，并将返回的文件ID与具体的业务实体关联方便后续的查询维护
     *
     * @param fileBusiType 文件关联的业务实体ID，如在自己表中维护，请传null
     * @param referenceId  文件所代表的业务类型，如在自己表中维护，请传null
     * @param fileUploadId 上传文件id
     * @param fileName     文件名称
     * @return boolean      是否操作成功
     * @throws
     */
//    public static boolean saveFileUploadInfo(final String referenceId, final Integer fileBusiType,
//                                             final String fileUploadId, final String fileName) throws Exception {
//        //首先对参数进行检查
//        if (fileUploadId == null) {
//            return false;
//        } else {
//            SysFileUploadInfo fileUploadInfo = new SysFileUploadInfo();
//            fileUploadInfo.setUploadId(CmUtil.getUUID());
//            fileUploadInfo.setFileUploadId(fileUploadId);
//            fileUploadInfo.setFileName(fileName);
//            fileUploadInfo.setReferenceId(referenceId);
//            fileUploadInfo.setFileBusiType(fileBusiType);
//            fileUploadInfo.setDelFlag(0);
//            fileUploadInfo.setCreatedBy(CmUtil.getUserId());
//            fileUploadInfo.setCreatedDate(CmUtil.date2day(null));
//            fileUploadInfo.setUpdatedBy(CmUtil.getUserId());
//            fileUploadInfo.setUpdatedDate(CmUtil.date2day(null));
//            // save fileUploadInfo entity in database
//            SysFileUploadInfoMapper fileUploadInfoMapper = (SysFileUploadInfoMapper) SpringContextUtil.getBean("sysFileUploadInfoMapper");
//            fileUploadInfoMapper.insertSelective(fileUploadInfo);
//        }
//        return true;
//    }

    /**
     * 方法说明：  根据业务类型和业务ID删除文件上传表中已上传的文件记录
     *
//     * @param fileBusiType 文件关联的业务实体ID
//     * @param referenceId  文件所代表的业务类型
     * @return
     * @throws
     */
//    public static void fileDeleteByRefId(String referenceId, Integer fileBusiType) throws Exception {
//        SysFileUploadInfoMapper fileUploadInfoMapper = (SysFileUploadInfoMapper) SpringContextUtil.getBean("sysFileUploadInfoMapper");
//        // 根据业务类型和业务ID删除文件上传表中已上传的文件记录
//        fileUploadInfoMapper.updateByRefIdSelective(referenceId, fileBusiType, 1, null, DateUtil.now());
//    }
//
//    public static boolean fileUpload(final String referenceId,
//                                     final Integer fileBusiType,
//                                     String sysUserId,
//                                     URL fileULR,
//                                     String fileName) throws Exception {
//        // 首先对参数进行检查
//        if (StrUtil.isNullOrUndefined(fileName)) {
//            return false;
//        } else {
//
//            HttpURLConnection connection = (HttpURLConnection) fileULR.openConnection();
//            InputStream inputStream = connection.getInputStream();
//            // Open a file using which we will then create a TusUpload. If you do not have
//            // a File object, you can manually construct a TusUpload using an InputStream.
//            // See the documentation for more information.
//            final TusUpload upload = new TusUpload();
//            upload.setInputStream(inputStream);
//            upload.setSize(connection.getContentLength());
//            upload.setFingerprint("stream");
//
//            logger.info("File upload start...");
//
//            // We wrap our uploading code in the TusExecutor class which will automatically catch
//            // exceptions and issue retries with small delays between them and take fully
//            // advantage of tus' resumability to offer more reliability.
//            // This step is optional but highly recommended.
//
//            TusExecutor executor = new TusExecutor() {
//                @Override
//                protected void makeAttempt() throws ProtocolException, IOException {
//                    // First try to resume an upload. If that's not possible we will create a new
//                    // upload and get a TusUploader in return. This class is responsible for opening
//                    // a connection to the remote server and doing the uploading.
//                    final TusUploader uploader = getTusClient().resumeOrCreateUpload(upload);
//
//                    // Upload the file in chunks of 1KB sizes.
//                    uploader.setChunkSize(1024);
//
//                    // Upload the file as long as data is available. Once the
//                    // file has been fully uploaded the method will return -1
//                    do {
//                        // Calculate the progress using the total size of the uploading file and
//                        // the current offset.
//                        long totalBytes = upload.getSize();
//                        long bytesUploaded = uploader.getOffset();
//                        double progress = 100;
//                        if (totalBytes != 0) {
//                            progress = (double) bytesUploaded / totalBytes * 100;
//                        }
//                        logger.info(String.format("Upload at %06.2f%% %n", progress));
//                    } while (upload.getSize() > 0 && uploader.uploadChunk() > -1);
//
//                    // Allow the HTTP connection to be closed and cleaned up
//                    uploader.finish();
//                    if (connection != null) {
//                        connection.disconnect();
//                    }
//                    logger.info("Upload finished.");
//                    logger.info(String.format("Upload available at: %s", uploader.getUploadURL().toString()));
//
//                    // when the file upload finish we should save information in database
//                    SysFileUploadInfo fileUploadInfo = new SysFileUploadInfo();
//                    // obtain upload file id  http://localhost:1080/files/233ac50120ebd1f2b12df3bdf44b6e90 file id is 233ac50120ebd1f2b12df3bdf44b6e90
//                    fileUploadInfo.setFileUploadId(StringUtils.substring(uploader.getUploadURL().toString(), StringUtils.lastIndexOf(uploader.getUploadURL().toString(), "/") + 1));
//                    fileUploadInfo.setFileName(fileName);
//                    fileUploadInfo.setReferenceId(referenceId);
//                    fileUploadInfo.setFileBusiType(fileBusiType);
//                    fileUploadInfo.setDelFlag(0);
//                    fileUploadInfo.setCreatedBy(sysUserId);
//                    fileUploadInfo.setCreatedDate(DateUtil.now());
//                    fileUploadInfo.setUpdatedBy(sysUserId);
//                    fileUploadInfo.setUpdatedDate(DateUtil.now());
//                    // save fileUploadInfo entity in database
//                    SysFileUploadInfoMapper fileUploadInfoMapper = (SysFileUploadInfoMapper) SpringContextUtil.getBean("sysFileUploadInfoMapper");
//                    fileUploadInfoMapper.insertSelective(fileUploadInfo);
//                }
//            };
//            executor.makeAttempts();
//        }
//        return true;
//    }

    public static String fileUpload(HttpURLConnection connection, String tusdUrl) throws Exception {
        InputStream inputStream = connection.getInputStream();
        final TusUpload upload = new TusUpload();
        upload.setInputStream(inputStream);
        upload.setSize(connection.getContentLength());
        upload.setFingerprint("stream");
        logger.info("File upload start...");
        final TusUploader uploader = getTusClient(tusdUrl).resumeOrCreateUpload(upload);
        uploader.setChunkSize(1024);
        do {
            long totalBytes = upload.getSize();
            long bytesUploaded = uploader.getOffset();
            double progress = 100;
            if (totalBytes != 0) {
                progress = (double) bytesUploaded / totalBytes * 100;
            }
            logger.info(String.format("Upload at %06.2f%% %n", progress));
        } while (upload.getSize() > 0 && uploader.uploadChunk() > -1);

        uploader.finish();
        connection.disconnect();
        logger.info("Upload finished.");
        logger.info(String.format("Upload available at: %s", uploader.getUploadURL().toString()));
        return uploader.getUploadURL().toString();
    }

    /**
     * @param url      请求的路径
     * @param filePath 文件将要保存的目录
     * @param method   请求方法，包括POST和GET
     * @param fileName 文件名
     * @return
     * @从制定URL下载文件并保存到指定目录
     */
    public static File saveUrlAs(String url, String filePath, String method, String fileName) {

        //创建不同的文件夹目录
        File file = new File(filePath);
        //判断文件夹是否存在
        if (!file.exists()) {
            //如果文件夹不存在，则创建新的的文件夹
            file.mkdirs();
        }
        FileOutputStream fileOut = null;
        HttpURLConnection conn = null;
        InputStream inputStream = null;
        BufferedOutputStream bos = null;
        BufferedInputStream bis = null;
        try {
            // 建立链接
            URL httpUrl = new URL(url);
            conn = (HttpURLConnection) httpUrl.openConnection();
            // 以Post方式提交表单，默认get方式
            conn.setRequestMethod(method);
            conn.setDoInput(true);
            conn.setDoOutput(true);
            // post方式不能使用缓存
            conn.setUseCaches(false);
            // 连接指定的资源
            conn.connect();
            // 获取网络输入流
            inputStream = conn.getInputStream();
            bis = new BufferedInputStream(inputStream);
            // 判断文件的保存路径后面是否以/结尾
            if (!filePath.endsWith(File.separator)) {
                filePath += File.separator;
            }
            while (fileName.indexOf(File.separator)>=0)   fileName=fileName.replace(File.separator,"");
            while (fileName.indexOf("..")>=0)   fileName=fileName.replace("..","");
            // 写入到文件（注意文件保存路径的后面一定要加上文件的名称）
            fileOut = new FileOutputStream(filePath + fileName);
            bos = new BufferedOutputStream(fileOut);

            byte[] buf = new byte[4096];
            int length = bis.read(buf);
            // 保存文件
            while (length != -1) {
                bos.write(buf, 0, length);
                length = bis.read(buf);
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("抛出异常！！");
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
            try {
                if (bos != null) {
                    bos.flush();
                    bos.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (bis != null) {
                    bis.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        while (fileName.indexOf(File.separator)>=0)   fileName=fileName.replace(File.separator,"");
        while (fileName.indexOf("..")>=0)   fileName=fileName.replace("..","");
        File retFlie = new File(filePath + fileName);
        return retFlie;

    }
}
